package myJs.user.workflowMission.featureSelectionResult

import com.softwaremill.quicklens._
import japgolly.scalajs.react.facade.SyntheticDragEvent
import myJs.Implicits._
import myJs.Utils.layer
import myJs.api.Api
import myJs.cps._
import myJs.myPkg._
import myJs.myPkg.reactHookForm._
import myJs.myPkg.topology._
import myJs.tool.Tool
import myJs.tool.Tool.messages
import myJs.user.workflow.featureSelection.AllFSSoft
import org.scalajs.dom.{html, window}
import org.scalajs.macrotaskexecutor.MacrotaskExecutor.Implicits._
import scalax.collection.GraphTraversal.BreadthFirst
import shared.Pojo.MetDataKind
import shared.SharedTool
import shared.pojo.Data.{GetNumericColNames, GetRowCols}
import shared.pojo.GroupFileData.GetGroups
import shared.pojo.Pojo._
import shared.pojo.Pojo.workflowMission._

import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.JSON
import myJs.myPkg.jquery._
import myJs.user.FeatureSelectionTool
import myJs.user.workflow.featureSelection.uniAna.UniAna
import japgolly.scalajs.react.vdom.all._
import japgolly.scalajs.react._
import japgolly.scalajs.react.React.Fragment
import japgolly.scalajs.react.util.EffectCatsEffect._
import myJs.myPkg.Hooks._
import cats.effect._
import myJs.Implicits._
import cats.syntax.all._
import myJs.myPkg.Swal.swal
import myJs.user.workflow.featureSelection.boruta.Boruta
import myJs.user.workflow.featureSelection.diagnoseAna.DiagnoseAna
import myJs.user.workflow.featureSelection.pathwayAna.PathwayAna
import myJs.user.workflow.featureSelection.rf.Rf
import myJs.user.workflow.featureSelection.svm.Svm

/** Created by yz on 3/11/2020
  */
object Cp {

  case class Props(fsData: FsData) {
    @inline def render: VdomElement = {
      Component(this)
    }
  }

  val curTool = FeatureSelectionResult
  var topology: Topology = _

  val Component = ScalaFnComponent[Props] { props =>
    val useForm = ReactHookForm.useForm(UseFormOptions(mode = "onChange"))

    val (softKind, setSoftKind) = useState("featureSelection")
    val (extraData, setExtraData) = useState(ExtraData())
    val (showIdStr, setShowIdStr) = useState("preprocess_0")
    val (draggable, setDraggable) = useState(false)
    val (exampleWorkflow, setExampleWorkflow) = useState("")
    val (fsData, setFsData) = useState(props.fsData)

    val fieldArrayMap = AllFSSoft.fieldArraySofts.map { x =>
      (x.nameStr, x.fieldArray(useForm))
    }.toSeqMap

    object FTool {

      def fsDataChange = (newData: FsData) => {
        setFsData(newData)
      }
      def fsDataModifyF = (newData: FsData => FsData) => {
        setFsData(newData)
      }
      def extraDataChange = (v: ExtraData) => {
        setExtraData(v)
      }

      def refreshDataFileNode = {
        SyncIO {
          val index = layer.load(1, Tool.layerOptions.shade(js.Array("0.1", "#fff")))
          val dataFileRect = {
            val centerX = 300
            val centerY = 80
            val halfWidth = 60
            val halfHeight = 20
            Rect(
              x = centerX - halfWidth,
              y = centerY - halfHeight,
              width = halfWidth * 2,
              height = halfHeight * 2,
              center = Point(
                x = centerX,
                y = centerY
              ),
              ex = centerX + halfWidth,
              ey = centerY + halfHeight
            )
          }
          val dataFileNode = TopologyNode(
            id = "dataFileNode",
            name = "rectangle",
            borderRadius = 0.1,
            paddingBottom = 10,
            paddingTop = 10,
            paddingLeft = 10,
            paddingRight = 10,
            text = messages("dataMatrix"),
            rect = dataFileRect,
            fullTextRect = dataFileRect,
            hideAnchor = true
          )
          topology.addNode(dataFileNode, focus = true)
          val preprocessRect = {
            val centerX = 300
            val centerY = 150
            val halfWidth = 60
            val halfHeight = 20
            Rect(
              x = centerX - halfWidth,
              y = centerY - halfHeight,
              width = halfWidth * 2,
              height = halfHeight * 2,
              center = Point(
                x = centerX,
                y = centerY
              ),
              ex = centerX + halfWidth,
              ey = centerY + halfHeight
            )
          }
          val preprocessNode = TopologyNode(
            id = "preprocess_0",
            name = "rectangle",
            borderRadius = 0.1,
            paddingBottom = 10,
            paddingTop = 10,
            paddingLeft = 10,
            paddingRight = 10,
            text = messages("cleaningAndProcessing"),
            rect = preprocessRect,
            fullTextRect = preprocessRect,
            anchorType = AnchorType.UpAndBottom
          )
          topology.addNode(preprocessNode, focus = true)
          val line = Line(
            from = Point(
              x = 300,
              y = 100,
              direction = Direction.Bottom,
              anchorIndex = 0,
              id = "dataFileNode"
            ),
            to = Point(
              x = 300,
              y = 130,
              direction = Direction.Up,
              anchorIndex = 0,
              id = "preprocess_0",
              autoAnchor = false
            ),
            name = "curve",
            controlPoints = js.Array(
              Point(
                x = 300,
                y = 110,
                direction = Direction.Bottom,
                anchorIndex = 0,
                id = "dataFileNode"
              ),
              Point(
                x = 300,
                y = 120,
                direction = Direction.Up,
                anchorIndex = 0,
                id = "1"
              )
            ),
            toArrow = "triangleSolid",
            locked = Lock.NoEvent
          )
          topology.addLine(line, focus = true)
          curTool.addPen2ActiveLayer(topology, preprocessNode.asInstanceOf[Pen])
          topology.render()
          layer.close(index)
        } >>
          setDraggable(true)
      }

      def getGroups = {
        val request = GetGroups.Request(file = fsData.file, groupFile = fsData.groupFile)
        Api.groupFileData.getGroups(request).map { response =>
          response
        }
      }

      def refreshExtraData = {
        (FTool.getGroups, FTool.getRowNames, FTool.getGroupHeaders).tupled.flatMapSync {
          case (groups, rowNames, headers) =>
            val newExtraData = extraData
              .modify(_.groups)
              .setTo(groups)
              .modify(_.rowNames)
              .setTo(rowNames)
              .modify(_.groupHeaders)
              .setTo(headers)
            setExtraData(newExtraData)
        }
      }

      def getRowNames = {
        val request = GetRowCols.Request(fileName = fsData.file)
        Api.data.getRowCols(request).map { response =>
          response.rowNames
        }
      }

      def getGroupHeaders = {
        val request = GetNumericColNames.Request(fileName = fsData.groupFile)
        Api.data.getNumericColNames(request).map { response =>
          response.headers.filterNot { x =>
            List("Sample", "ClassNote", "Pair_ID").contains(x)
          }
        }
      }

    }

    useEffect(
      {
        SyncIO {
          val topologyOptions = TopologyOptions(
            hideRotateCP = true,
            hideSizeCP = true,
            disableScale = true,
            disableEmptyLine = true,
            autoAnchor = false,
            hideAnchor = false,
            rotateCursor = "pointer",
            hoverCursor = "pointer",
            disableDockLine = true,
            hideInput = true,
            anchorFillStyle = "red"
          )
          topology = Topology(
            parent = "topo-canvas",
            options = topologyOptions
          )
          if (Tool.getMissionId.isEmpty) {
            topology.open(js.Dictionary("grid" -> true))
          }
          topology.on("space", () => {})
          topology.on("line", (pen: MyPen) => {})
          topology.myOnAsync(
            "node",
            (pen: MyPen) => {
              val node = pen.toPen.toNode
              if (messages("dataMatrix") != node.text) {
                val curSoft = curTool.getCurSoft(node.id)
                val notRunIO = if (List(PathwayAna, Rf, DiagnoseAna, Svm, Boruta).contains(curSoft)) {
                  Api.workflowMission
                    .inputMetabolites(InputMetabolitesRq(missionId = Tool.getMissionId.toInt, idStr = node.id))
                    .map { metabolites =>
                      metabolites.isEmpty
                    }
                } else IO(false)
                notRunIO.flatMapSync { notRun =>
                  if (!notRun) {
                    $(".totalNodeText").show()
                    SharedTool.getKind(node.id) match {
                      case x if x == UniAna.nameStr =>
                        FeatureSelectionTool.groups = fsData.uniAna.find(_.nodeId == node.id).get.uniAnaGroups
                      case _ =>
                    }
                    curTool.selectPen(topology, pen.toPen)
                    setShowIdStr(node.id)
                  } else
                    SyncIO {
                      $(".result").hide()
                      swal(
                        SwalOptions(
                          title = "Error",
                          text = "No input metabolites found! The module not run! ",
                          `type` = "error"
                        )
                      )
                    }
                }
              } else {
                setShowIdStr("").to[IO]
              }
            }
          )
          topology.myOn(
            "delete",
            (myPen: MyPen) => {
              setShowIdStr("")
            }
          )
          val graphDataDict = JSON.parse(fsData.graphData).toJDJAn
          topology.open(graphDataDict)
          useForm.reset(fsData)
          val otherNodes = curTool.getOtherNodes(topology)
          curTool.refreshGroups(otherNodes.head.id)
          curTool.refreshRowNames(otherNodes.head.id)
          curTool.refreshGroupHeaders(otherNodes.head.id)
          val graph = curTool.getGraph(topology)
          val initSelectNode = (graph.get("dataFileNode")).withKind(BreadthFirst).toList(1)
          val selectedPen = topology.find(initSelectNode).toPen
          curTool.selectPen(topology, selectedPen)
          curTool.getPenLines(topology).foreach { pen =>
            pen.locked = Lock.NoEvent
          }
          topology.lockPens(topology.find("dataFileNode").toPens, Lock.NoEvent)
          topology.lockPens(curTool.getPenNodes(topology), Lock.Readonly)
          val needDiffNodeIds = curTool
            .getAllNodes(topology)
            .map { pen =>
              pen.id
            }
            .filter { idStr =>
              idStr == "dataFileNode" || {
                val curSoft = curTool.getCurSoft(idStr)
                !curTool.lastSofts.contains(curSoft)
              }
            }
          curTool.getDiffNum(needDiffNodeIds).foreach { dict =>
            dict.foreach { case (idStr, diffNum) =>
              val node = topology.find(idStr).toPen.toNode
              node.text = s"${node.text}(${diffNum})"
            }
            topology.render()
          }
          $("#args").find(":input").setDisabled(true)
          ()
        }
//        >>
//        FTool.refreshDataFileNode
      },
      List()
    )

    useEffect(
      {
        if (fsData.file.nonBlank && fsData.groupFile.nonBlank) {
          FTool.refreshExtraData
        } else IO {}
      },
      List(fsData.file, fsData.groupFile)
    )

    FormProvider(
      trigger = useForm.trigger _,
      control = useForm.control,
      formState = useForm.formState,
      setValue = useForm.setValue _,
      watch = useForm.watch _,
      getFieldState = useForm.getFieldState _
    )(
      form(
        myJs.user.workflow.featureSelection.preprocess.Cp
          .Props(
            fsData = fsData,
            fsDataChange = FTool.fsDataChange,
            showIdStr = showIdStr,
            extraData = extraData
          )
          .render,
        AllFSSoft.fieldArraySofts.toVdomArray { soft =>
          div(
            key := s"${soft.nameStr}",
            soft.cp(
              fieldArray = fieldArrayMap(soft.nameStr),
              fsData = fsData,
              fsDataChange = FTool.fsDataChange,
              fsDataModifyF = FTool.fsDataModifyF,
              showIdStr = showIdStr,
              extraData = extraData,
              extraDataChange = FTool.extraDataChange
            )
          )
        }
      )
    )
  }

}
